// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023.
 * Description: KVM: arm64:vgic-v3: support virq set affinity to phys cpu
 */

bool vgic_v3_affinity_prot(struct kvm *kvm)
{
	if (!kvm_vgic_global_state.has_gicv4 &&
		virq_can_affinity && kvm->irq_auto_bind)
		return true;
	else
		return false;
}

static struct vgic_its *v3_vgic_get_its(struct kvm *kvm,
				struct kvm_kernel_irq_routing_entry *irq_entry)
{
	struct kvm_msi msi  = (struct kvm_msi) {
		.address_lo     = irq_entry->msi.address_lo,
		.address_hi     = irq_entry->msi.address_hi,
		.data           = irq_entry->msi.data,
		.flags          = irq_entry->msi.flags,
		.devid          = irq_entry->msi.devid,
	};

	return vgic_msi_to_its(kvm, &msi);
}

int kvm_vgic_set_forwarding(struct kvm *kvm, int virq,
			    struct kvm_kernel_irq_routing_entry *irq_entry)
{
	struct vgic_its *its;
	struct vgic_irq *irq;
	struct kvm_vcpu *vcpu;
	int ret;
	int cpu = -1;

	if (!vgic_has_its(kvm))
		return 0;
	/*
	 * Get the ITS, and escape early on error (not a valid
	 * doorbell for any of our vITSs).
	 */
	its = v3_vgic_get_its(kvm, irq_entry);
	if (IS_ERR(its))
		return 0;

	mutex_lock(&its->its_lock);
	vcpu = ite_get_vcpu(kvm, its, irq_entry->msi.devid,
			    irq_entry->msi.data);
	if (!vcpu) {
		ret = E_ITS_INT_UNMAPPED_INTERRUPT;
		goto out;
	}

	/* Get the virq from the actual DevID/EventID -> LPI translation. */
	ret = ite_get_irq(kvm, its, irq_entry->msi.devid,
			  irq_entry->msi.data, &irq);
	if (ret || irq == NULL)
		goto out;

	/* Avoid add same irq affinity */
	if (irq->host_irq || irq->host_irq == virq)
		goto out;

	irq->hw         = false;
	irq->host_irq   = virq;

	if (vcpu->cpu != -1)
		cpu = vcpu->cpu;
	else if (vcpu->pre_pcpu != -1)
		cpu = vcpu->pre_pcpu;

	if (cpu != -1)
		irq_set_affinity(irq->host_irq, cpumask_of(cpu));

out:
	mutex_unlock(&its->its_lock);
	return ret;
}

int kvm_vgic_unset_forwarding(struct kvm *kvm, int virq,
			      struct kvm_kernel_irq_routing_entry *irq_entry)
{
	struct vgic_its *its;
	struct vgic_irq *irq;
	struct kvm_vcpu *vcpu;
	int ret;

	if (!vgic_has_its(kvm))
		return 0;

	/*
	 * Get the ITS, and escape early on error (not a valid
	 * doorbell for any of our vITSs).
	 */
	its = v3_vgic_get_its(kvm, irq_entry);
	if (IS_ERR(its))
		return 0;

	mutex_lock(&its->its_lock);

	vcpu = ite_get_vcpu(kvm, its, irq_entry->msi.devid,
			    irq_entry->msi.data);
	if (!vcpu) {
		ret = E_ITS_INT_UNMAPPED_INTERRUPT;
		goto out;
	}

	ret = ite_get_irq(kvm, its, irq_entry->msi.devid,
			  irq_entry->msi.data, &irq);
	if (ret || irq == NULL || irq->host_irq == 0)
		goto out;

	WARN_ON(!(irq->host_irq == virq));
	if (irq->host_irq)
		irq->host_irq = 0;

out:
	mutex_unlock(&its->its_lock);
	return ret;
}

